package com.spark.common.net

import android.annotation.SuppressLint
import android.net.Uri
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.NetworkUtils
import com.orhanobut.logger.Logger
import com.spark.common.Const
import io.reactivex.Observable
import io.reactivex.annotations.Nullable
import okhttp3.*
import okhttp3.logging.HttpLoggingInterceptor
import okio.Buffer
import org.json.JSONObject
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.scalars.ScalarsConverterFactory
import retrofit2.http.*
import java.io.File
import java.net.Proxy
import java.util.concurrent.TimeUnit
import kotlin.properties.Delegates

@SuppressLint("NewApi")
object Http {
    fun request(logger: Boolean = true,
                params: MutableMap<String, String>? = null,
                headers: MutableMap<String?, String?>? = mutableMapOf(
                        Pair("cookie", "safety.session.id=${Const.token()}")
                ),
                maxStale: Long = 0,
                maxAge: Long = 0,
                progressListener: ProgressBody.ProgressListener? = null): API {
        val ob = okhttp()
        if (logger) ob.addInterceptor(loogerInterceptor())
        if (params?.isNotEmpty() == true) ob.addInterceptor(paramsInterceptor(params))
        if (headers?.isNotEmpty() == true) ob.addInterceptor(headerInterceptor(headers))
        if (maxStale != 0L || maxAge != 0L) ob.addInterceptor(netCacheInterceptor(maxStale, maxAge))
        if (progressListener != null) ob.addNetworkInterceptor(progressInterceptor(progressListener))
        return Retrofit.Builder()
                .client(ob.build())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(ScalarsConverterFactory.create())
                .baseUrl(Const.base_url)
                .build()
                .create(API::class.java)
    }

    /**
     * 创建 okhttpClient
     */
    private fun okhttp(): OkHttpClient.Builder {
        return OkHttpClient.Builder()
                .retryOnConnectionFailure(true)
                .connectTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .proxy(Proxy.NO_PROXY)
    }


    /**
     * 网络请求日志
     */
    private fun loogerInterceptor(): HttpLoggingInterceptor {
        val logger = HttpLoggingInterceptor.Logger {
            // Platform.get().log(Platform.INFO, "【okhttp】$it", null)
            Logger.e("【okhttp】", it)
        }
        val httpLoggingInterceptor = HttpLoggingInterceptor(logger)
        httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
        return httpLoggingInterceptor
    }

    /**
     * builder/post的公共参数部分
     * 添加公共参数
     */

    private fun paramsInterceptor(params: MutableMap<String, String>): Interceptor {
        return Interceptor {
            var request = it.request()
            var url = request.url().toString()
            val method = request.method()
            val originRequestBody = request.body()
            when (method) {
                "GET" -> {
                    val uri = Uri.parse(url)
                    val oldParams = uri.queryParameterNames
                    params.forEach { k, v ->
                        if (!oldParams.contains(k)) {
                            url += if (oldParams.size > 0) {
                                "$k=$v"
                            } else {
                                "?$k=$v"
                            }
                        }
                    }
                    request = request
                            .newBuilder()
                            .url(url)
                            .build()
                }
                "POST" -> {
                    if (originRequestBody is FormBody) {
                        val formBodyBuilder = FormBody.Builder()
                        var formBody = originRequestBody
                        for (i in 0..formBody.size()) {
                            formBodyBuilder.add(formBody.encodedName(i), formBody.encodedValue(i))
                        }
                        params.forEach { k, v ->
                            formBodyBuilder.add(k, v)
                        }
                        formBody = formBodyBuilder.build()
                        request = request.newBuilder()
                                .post(formBody)
                                .build()
                    }
                }
            }
            return@Interceptor it.proceed(request)
        }
    }

    /**
     * builder / post 添加请求头
     */

    private fun headerInterceptor(headers: MutableMap<String?, String?>): Interceptor {
        return Interceptor {
            val requestBuilder = it.request().newBuilder()
            headers.forEach { k, v ->
                if (!v.isNullOrBlank())
                    requestBuilder.addHeader(k, v)
                // LogUtils.e("【header】【k】$k 【v】$v")
            }
            return@Interceptor it.proceed(requestBuilder.build())
        }
    }

    /**
     * 网络缓存拦截器
     */
    private fun netCacheInterceptor(maxAge: Long = 0, maxStale: Long = 0): Interceptor {
        var response by Delegates.notNull<Response>()
        var headerValue by Delegates.notNull<String>()
        return Interceptor {
            var request = it.request()
            if (NetworkUtils.isConnected()) {
                response = it.proceed(request)
                headerValue = "public,max-age=$maxAge"
            } else {
                request = request
                        .newBuilder()
                        .cacheControl(CacheControl.FORCE_CACHE)
                        .build()
                response = it.proceed(request)
                headerValue = "public,only-if-cached,max-stale=$maxStale"
            }
            val contentType = response.body()?.contentType()
            val content = response.body()?.string() ?: ""
            return@Interceptor response
                    .newBuilder()
                    .removeHeader("Pragma")
                    .removeHeader("Cache-Control")
                    .body(ResponseBody.create(contentType, content))
                    .build()
        }
    }

    private fun logInterceptor(): Interceptor {
        return Interceptor {
            val request = it.request()
            val buffer = Buffer()
            request.body()?.writeTo(buffer)
            LogUtils.e("【params】${buffer.readByte()}")
            return@Interceptor it.proceed(request)
        }
    }

    /**
     * 进度监听拦截器
     */
    private fun progressInterceptor(progressListener: ProgressBody.ProgressListener?, refreshTime: Long = 1000L): Interceptor {
        return Interceptor {
            val originResponse = it.proceed(it.request())
            return@Interceptor originResponse.newBuilder()
                    .body(ProgressBody.response(originResponse.body(), progressListener, refreshTime))
                    .build()
        }
    }

    /**
     * 异常处理：404
     */
/*    private fun exceptionInterceptor(): Interceptor {
        return Interceptor { }
    }*/
}

interface API {
    // <><><><><><><><><>builder
    @GET
    fun rxGet(@Url url: String,
              @QueryMap @Nullable params: Map<String, Any>?): Observable<String>

    @GET
    fun rxGet(@Url url: String): Observable<String>


    @GET
    fun get(@Url url: String,
            @QueryMap @Nullable params: Map<String, String>?): Call<String>

    @GET
    fun get(@QueryMap @Nullable params: Map<String, String>?): Call<String>

    // <><><><><><><><><>post
    @FormUrlEncoded
    @POST()
    fun rxPost(@Url url: String,
               @FieldMap @Nullable params: MutableMap<String, String>?): Observable<String>

    @FormUrlEncoded
    @POST()
    fun rxPost(@FieldMap @Nullable params: MutableMap<String, String>?): Observable<String>

    @POST()
    fun rxPost(@Url url: String,
               @Nullable @Body requestBody: RequestBody): Observable<String>

    @POST()
    fun rxPost(@Url url: String): Observable<String>

    @FormUrlEncoded
    @POST()
    fun post(@Url url: String,
             @FieldMap @Nullable params: Map<String, String>?): Call<String>

    @FormUrlEncoded
    @POST()
    fun post(@FieldMap @Nullable params: Map<String, String>?): Call<String>

    // <><><><><><><><><>上传
    @Multipart
    @POST()
    fun rxUpload(@Url url: String,
                 @Part("description") @Nullable desciption: RequestBody?,
                 @Part multipart: MultipartBody.Part): Observable<String>

    @Multipart
    @POST()
    fun rxUpload(@Url url: String,
                 @Part multipart: MultipartBody.Part): Observable<String>


    @Multipart
    @POST()
    fun upload(@Url url: String,
               @Part("description") @Nullable desciption: RequestBody?,
               @Part multipart: MultipartBody.Part): Call<String>

    @Multipart
    @POST()
    fun upload(@Part("description") @Nullable desciption: RequestBody?,
               @Part multipart: MultipartBody.Part): Call<String>

    // <><><><><><><><><>批量上传
    @Multipart
    @POST()
    fun rxUploads(@Url url: String,
                  @Part files: List<MultipartBody.Part>): Observable<String>

    @Multipart
    @POST()
    fun rxUploads(@Part files: List<MultipartBody.Part>): Observable<String>

    @Multipart
    @POST()
    fun uploads(@Url url: String,
                @Part files: List<MultipartBody.Part>): Call<String>

    @Multipart
    @POST()
    fun uploads(@Part files: List<MultipartBody.Part>): Call<String>
}

/**
 * ApiType MediaType
 * Form
 * Form-Url-Encoded
 * Json
 */
object ApiType {
    const val json = "application/json;charset=utf-8"
}

/**
 * <><><><><><>上传文件扩展<><><><><><><><><><>
 */

/**
 * 文件转为 multipartbody.part 格式
 */
fun File.toFileBody(fileKey: String): MultipartBody.Part {
    val wrapperFileBody = RequestBody.create(MultipartBody.FORM, this)
    return MultipartBody.Part.createFormData(fileKey, this.name, wrapperFileBody)
}

/**
 * String 转为 multipartbody.part 格式
 */
fun String.toFileBody(fileKey: String): MultipartBody.Part {
    return File(this).toFileBody(fileKey)
}

/**
 * 上传 Json 格式的表单
 */
fun String.toJsonBody(): RequestBody {
    return RequestBody.create(MediaType.parse(ApiType.json), this)
}

@SuppressLint("NewApi")
fun <K, V> MutableMap<K, V>.toJsonBody(): RequestBody {
    val jo = JSONObject()
    this.forEach { k, v ->
        jo.put(k.toString(), v.toString())
    }
    LogUtils.e("jo >>> $jo")
    return RequestBody.create(MediaType.parse(ApiType.json), jo.toString())
}

/**
 * post 上传表单
 */
@SuppressLint("NewApi")

fun MutableMap<String, String>.toFormBody(): RequestBody {
    val builder = FormBody.Builder()
    forEach { k, v ->
        builder.add(k, v)
    }
    return builder.build()
}